package top.luoqiz.jenkins.config.sms;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
import top.luoqiz.jenkins.config.sms.store.InMemoryVerificationCodeStore;
import top.luoqiz.jenkins.config.sms.store.VerificationCodeStore;

/**
 * @author luoqiz
 */
public class DaoSmsVerificationCodeAuthenticationProvider extends AbstractSmsVerificationCodeAuthenticationProvider {


    // ~ Instance fields
    // ================================================================================================


    private UserDetailsService userDetailsService;

    private VerificationCodeStore verificationCodeStore = new InMemoryVerificationCodeStore();

    // ~ Methods
    // ========================================================================================================

    @Override
    @SuppressWarnings("deprecation")
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  SmsVerificationCodeAuthenticationToken authentication)
            throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }

        boolean validate = verificationCodeStore.verification(authentication.getPrincipal().toString(), authentication.getCredentials().toString());
        if (!validate) {
            logger.debug("Authentication failed: verification code does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }

    @Override
    protected void doAfterPropertiesSet() {
        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
    }

    @Override
    protected final UserDetails retrieveUser(String username,
                                             SmsVerificationCodeAuthenticationToken authentication)
            throws AuthenticationException {
        try {
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        }
//		catch (MobileNotFoundException ex) {
//			mitigateAgainstTimingAttack(authentication);
//			throw ex;
//		}
        catch (InternalAuthenticationServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal,
                                                         Authentication authentication, UserDetails user) {
        return super.createSuccessAuthentication(principal, authentication, user);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    protected UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setVerificationCodeStore(VerificationCodeStore verificationCodeStore) {
        this.verificationCodeStore = verificationCodeStore;
    }

    protected VerificationCodeStore getVerificationCodeStore() {
        return verificationCodeStore;
    }
}
